package at.jaki.ubb.crawling.core;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.apache.log4j.Logger;

/**
 * A singleton. This pool will handle all executing threads.
 * 
 * @author Attila Jakabfi
 * 
 */
public class ThreadPool {

	private static final Logger LOG = Logger.getLogger(ThreadPool.class);

	/**
	 * the number of threads that only work for a little time, and then go back
	 * to sleep.
	 */
	private static final int SLEEPER_THREAD_COUNT = 5;

	private static ThreadPool instance;
	private static Integer threadNumber;

	/**
	 * Will return the instance of the {@link ThreadPool}
	 * 
	 * @return
	 */
	public static synchronized ThreadPool getInstance() {
		if (instance == null || instance.isStopped()) {
			instance = new ThreadPool(threadNumber);
		}
		return instance;
	}

	/**
	 * Sets the number of threads available in the thread pool. In case of null,
	 * the number of cores will be used.
	 * 
	 * @param poolSize can be null.
	 */
	public static void setPoolSize(Integer poolSize) {
		threadNumber = poolSize;
	}

	private ExecutorService pool;

	/**
	 * @param maxThreads in case of null, the number of maximum threads will be
	 *        number of cores on the host machine.
	 */
	private ThreadPool(Integer maxThreads) {
		if (maxThreads == null) {
			maxThreads = Runtime.getRuntime().availableProcessors() + SLEEPER_THREAD_COUNT - 1;
		}
		LOG.info("Createing thread pool of size: " + maxThreads);
		pool = Executors.newFixedThreadPool(maxThreads);
	}

	/**
	 * Will return the {@link ExecutorService} element.
	 * 
	 * @return can't be null.
	 */
	private ExecutorService getPool() {
		return pool;
	}

	private boolean isStopped() {
		return getPool().isShutdown() || getPool().isTerminated();
	}

	/**
	 * Will add a task to the pool.
	 * 
	 * @param task task object.
	 * @return the {@link Future} object
	 */
	public Future<?> submit(Callable<?> task) {
		LOG.debug("submitting task: " + task);
		return getPool().submit(task);
	}

	/**
	 * Will add a task to the pool.
	 * 
	 * @param task task object.
	 */
	public void submit(Runnable task) {
		LOG.debug("submitting task: " + task);
		getPool().submit(task);
	}

	/**
	 * Will terminate the thread pool.
	 */
	public void shutdown() {
		getPool().shutdown();
	}
}
